package seismosurfer.layer;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Properties;

import javax.swing.JMenuItem;
import javax.swing.JSeparator;

import seismosurfer.event.InfoMouseMode;
import seismosurfer.event.SeismoMapMouseInterpreter;
import seismosurfer.event.SeismoNavMouseMode;
import seismosurfer.graphics.SeismoDrawingAttributes;
import seismosurfer.gui.menuitem.DBMenuItem;
import seismosurfer.gui.menuitem.NNMenuItem;

import com.bbn.openmap.LatLonPoint;
import com.bbn.openmap.MapBean;
import com.bbn.openmap.MouseDelegator;
import com.bbn.openmap.event.MapMouseEvent;
import com.bbn.openmap.layer.OMGraphicHandlerLayer;
import com.bbn.openmap.omGraphics.OMGraphic;
import com.bbn.openmap.omGraphics.event.MapMouseInterpreter;
import com.bbn.openmap.proj.Proj;
import com.bbn.openmap.proj.Projection;
import com.bbn.openmap.util.PropUtils;

/**
 * An extension to OMGraphicHandlerLayer, this class is
 * the base layer of Seismo-Surfer from which most layers extend.
 *
 */
public abstract class SeismoLayer extends OMGraphicHandlerLayer {

    public static final String subLayersProperty = "subLayers";

    public final static String infoMouseMode = InfoMouseMode.modeID;

    public final static String navMouseMode = SeismoNavMouseMode.modeID;

    public final static String seismoMouseModes[] = { navMouseMode,
            infoMouseMode };

    public final static String updateInfoLine = "Updating Layer Data...";

    public final static String emptyInfoLine = "   ";
    
    protected MouseDelegator mouseDel = null;

    /** The DrawingAttributes object to describe the rendering of graphics. */
    protected SeismoDrawingAttributes seismoAttributes;

    protected java.util.List seismoAttributesList = new ArrayList();

    protected NNMenuItem nnMI = null;

    protected DBMenuItem dbMI = null;

    protected java.util.List mapMenuItems = new ArrayList();

    protected LatLonPoint pointCoords = null;

    public SeismoLayer() {

        setMouseModeIDsForEvents(seismoMouseModes);

    }
    
    /**
     * Creates OMGraphic objects from data related
     * to this layer, organizes them in a list 
     * and makes the necessary superclass calls, to 
     * render them on screen.
     *
     */
    public abstract void loadGraphics();
    
    /**
     * Overriden to use internally the SeismoMapMouseInterpreter.
     */
    public synchronized MapMouseInterpreter getMouseEventInterpreter() {
        if (getMouseModeIDsForEvents() != null && mouseEventInterpreter == null) {
            setMouseEventInterpreter(new SeismoMapMouseInterpreter(this));
        }

        return mouseEventInterpreter;
    }

    /**
     * Initializes this layer from the given properties.
     * 
     * @param props
     *            the <code>Properties</code> holding settings for this layer
     */
    public void setProperties(String prefix, Properties props) {
        super.setProperties(prefix, props);        

        setSeismoProperties(prefix, props);

        setMouseModeIDsForEvents(seismoMouseModes);
    }
    
    /**
     * Reads the layer (and sublayers) properties (recursively)
     * and hands them to a SeismoDrawingAttributes object.
     * 
     * @param prefix the prefix of a property name
     * @param props the Properties object that contains the properties
     *        of a (sub)layer
     */
    public void setSeismoProperties(String prefix, Properties props) {        

        seismoAttributes = new SeismoDrawingAttributes();
        if (seismoAttributesList.size() > 0) {
            seismoAttributes
                    .setFrom((SeismoDrawingAttributes) seismoAttributesList
                            .get(seismoAttributesList.size() - 1));
        }

        seismoAttributes.setProperties(prefix, props);        

        seismoAttributesList.add(seismoAttributes);

        prefix = PropUtils.getScopedPropertyPrefix(prefix);        

        java.util.List subLayers = PropUtils.parseSpacedMarkers(props
                .getProperty(prefix + subLayersProperty));        

        if (!subLayers.isEmpty()) {

            for (int k = subLayers.size() - 1; k >= 0; k--) {

                String newPrefix = prefix
                        + PropUtils.getScopedPropertyPrefix((String) subLayers
                                .get(k));

                setSeismoProperties(newPrefix, props);
            }

        }

    }
    
    /**
     * Returns true.
     */
    public boolean receivesMapEvents() {
        return true;
    }
    
    /**
     * If the mouse mode is SeismoNavMouseMode the left click is 
     * interpreted as a right click.
     */
    public boolean leftClick(MapMouseEvent mme) {

        if (getActiveMouseModeID().equals(navMouseMode)) {
            getMouseEventInterpreter().rightClick(mme);
        }

        return true;
    }
    
    /**
     * Gets the identifier of the active (selected by the user)
     * mouse mode.
     * 
     * @return the identifier of the active (selected by the user)
     *         mouse mode.
     */
    public String getActiveMouseModeID() {
        if (mouseDel != null) {
            return mouseDel.getActiveMouseModeID();
        } else {
            System.out.println("\nNULL MouseDelegator!!!");
            return null;
        }
    }
    
    /**
     * An OMGraphic is highlightable only if 
     * the InfoMouseMode is active.
     */
    public boolean isHighlightable(OMGraphic omg) {
        boolean highlight = false;

        if (getActiveMouseModeID().equals(infoMouseMode)) {
            return highlight = true;
        }
        
        return highlight;
    }
    
    public String getInfoText(OMGraphic omg) {

        if (getActiveMouseModeID().equals(infoMouseMode)) {

            return "Click for feature attributes.";

        } else if (getActiveMouseModeID().equals(navMouseMode)) {

            return "Click for more options.";

        }

        return null;
    }
    
    /**
     * Gives the coordinates of the "click" 
     * to the NNMenuItem and DBMenuItem components.
     * 
     * @param mme the MapMouseEvent from which 
     *        the coordinates are taken
     */
    public void setClickCoordinates(MapMouseEvent mme) {

        pointCoords = mme.getLatLon();

        if (nnMI != null) {
            nnMI.setClickCoordinates(pointCoords);
        }

        if (dbMI != null) {
            dbMI.setClickCoordinates(pointCoords);
        }

    }
    
    /**
     * Gets the coordinates of the last click on the map.
     * 
     * @return the coordinates as a LatLonPoint object
     */
    public LatLonPoint getClickCoordinates() {
        return pointCoords;
    }
    
    
    public java.util.List getItemsForMapMenu(MapMouseEvent mme) {

        setClickCoordinates(mme);        

        Object obj = mme.getSource();
        if (!(obj instanceof MapBean)) {
            return null;
        }

        final MapBean map = (MapBean) obj;

        if (mapMenuItems.isEmpty()) {

            JMenuItem centerPopMI = new JMenuItem("Center Map", KeyEvent.VK_C);
            centerPopMI.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    map.setCenter(getClickCoordinates());
                }
            });
            mapMenuItems.add(centerPopMI);

            JMenuItem ZoomInMenuItem = new JMenuItem("Zoom In", KeyEvent.VK_O);
            ZoomInMenuItem.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    Projection projection = map.getProjection();
                    Proj p = (Proj) projection;

                    p.setScale(p.getScale() / 2.0f);
                    p.setCenter(getClickCoordinates());

                    map.setProjection(p);
                }
            });

            mapMenuItems.add(ZoomInMenuItem);

            JMenuItem ZoomOutMenuItem = new JMenuItem("Zoom Out", KeyEvent.VK_O);
            ZoomOutMenuItem.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {

                    Projection projection = map.getProjection();
                    Proj p = (Proj) projection;

                    p.setScale(p.getScale() * 2.0f);
                    p.setCenter(getClickCoordinates());

                    map.setProjection(p);
                }
            });

            mapMenuItems.add(ZoomOutMenuItem);

            mapMenuItems.add(new JSeparator());

            if (nnMI != null) {
                JMenuItem nnPopMI = new JMenuItem();
                nnPopMI.setAction(nnMI.getMenuItemAction());
                nnPopMI.setText("NN Query");
                nnPopMI.setMnemonic('N');
                mapMenuItems.add(nnPopMI);
            }

            if (dbMI != null) {
                JMenuItem dbPopMI = new JMenuItem();
                dbPopMI.setAction(dbMI.getMenuItemAction());
                dbPopMI.setText("Distance Query");
                dbPopMI.setMnemonic('D');
                mapMenuItems.add(dbPopMI);
            }
        }

        return mapMenuItems;
    }
    
    /**
     * Return a java.util.List containing input for a JMenu with 
     * contents applicable to a popup menu for a location over an OMGraphic.
     * 
     * @param omg the OMGraphic for which the pop-up menu 
     *        will be constructed
     * @param mme the corresponding MapMouseEvent
     * @return a List containing options for the given OMGraphic.
     */
    public java.util.List getItemsForOMGraphicMenu(OMGraphic omg,
            MapMouseEvent mme) {
        return getItemsForMapMenu(mme);        
    }

    /**
     * If the mouse mode is SeismoNavMouseMode the left click is 
     * interpreted as a right click. This is a left click
     * on an OMGraphic.     
     *  
     * @param omg the OMGraphic on which the left click 
     *        occurred
     * @param mme the corresponding MapMouseEvent 
     * @return true of this information is to be considered 
     *         consumed and should not be passed to anybody else.
     */
    public boolean leftClick(OMGraphic omg, MapMouseEvent mme) {

        if (getActiveMouseModeID().equals(navMouseMode)) {
            getMouseEventInterpreter().rightClick(omg, mme);
        }
        
        return true;
    }

    public void findAndInit(Object someObj) {

        super.findAndInit(someObj);

        if (someObj instanceof NNMenuItem) {
            nnMI = (NNMenuItem) someObj;
        }

        if (someObj instanceof DBMenuItem) {
            dbMI = (DBMenuItem) someObj;
        }
        
        if (someObj instanceof MouseDelegator) {
            mouseDel = (MouseDelegator) someObj;
        }
        
    }

    public void findAndUndo(Object someObj) {

        super.findAndUndo(someObj);

        if (someObj instanceof NNMenuItem && someObj == nnMI) {
            nnMI = null;
        }

        if (someObj instanceof DBMenuItem && someObj == dbMI) {
            dbMI = null;
        }        

        if (someObj instanceof MouseDelegator && someObj == mouseDel) {
            mouseDel = null;
        }

    }

}
